Introduce gtk_window_get/set_attached_to()
authorAndrea Cimitan <andrea.cimitan@gmail.com>
Thu, 12 Jan 2012 18:29:52 +0000 (13:29 -0500)
committerCosimo Cecchi <cosimoc@gnome.org>
Thu, 12 Jan 2012 19:03:00 +0000 (14:03 -0500)
gtk_window_get/set_attached_to() is a new API that allows for windows to
be attached to a GtkWidget.
The attachment is a logical binding between the toplevel window and the
widget that generated it; this kind of information is currently used to
propagate style information from the widget to the window, but is also
useful e.g. for accessibility.

https://bugzilla.gnome.org/show_bug.cgi?id=666103

docs/reference/gtk/gtk3-sections.txt
gtk/gtk.symbols
gtk/gtkmenu.c
gtk/gtkwidget.c
gtk/gtkwidgetprivate.h
gtk/gtkwindow.c
gtk/gtkwindow.h

index f614e684e41b72ccea53b8af5be79e1f3748b0ce..3b2317e13679cfb1d29c2e7901c3e6e2d1699275 100644 (file)
@@ -5294,6 +5294,7 @@ gtk_window_set_gravity
 gtk_window_get_gravity
 gtk_window_set_position
 gtk_window_set_transient_for
+gtk_window_set_attached_to
 gtk_window_set_destroy_with_parent
 gtk_window_set_hide_titlebar_when_maximized
 gtk_window_set_screen
@@ -5352,6 +5353,7 @@ gtk_window_get_role
 gtk_window_get_size
 gtk_window_get_title
 gtk_window_get_transient_for
+gtk_window_get_attached_to
 gtk_window_get_type_hint
 gtk_window_get_skip_taskbar_hint
 gtk_window_get_skip_pager_hint
index 59df40ce7a2094e5719766a19f9ef25cb65f1af8..5fbeeb87ae9a99bfa100fe88c899b2bcf9acdb65 100644 (file)
@@ -3820,6 +3820,7 @@ gtk_window_get_skip_pager_hint
 gtk_window_get_skip_taskbar_hint
 gtk_window_get_title
 gtk_window_get_transient_for
+gtk_window_get_attached_to
 gtk_window_get_type
 gtk_window_get_type_hint
 gtk_window_get_urgency_hint
@@ -3891,6 +3892,7 @@ gtk_window_set_skip_taskbar_hint
 gtk_window_set_startup_id
 gtk_window_set_title
 gtk_window_set_transient_for
+gtk_window_set_attached_to
 gtk_window_set_type_hint
 gtk_window_set_urgency_hint
 gtk_window_set_wmclass
index 40a841d5b5596f8b64e4a6c6a50e34808828149d..03ad97030c6c3116d8351525a7aaa635744dd4e7 100644 (file)
@@ -1202,9 +1202,8 @@ gtk_menu_attach_to_widget (GtkMenu           *menu,
   if (gtk_widget_get_state_flags (GTK_WIDGET (menu)) != 0)
     gtk_widget_set_state_flags (GTK_WIDGET (menu), 0, TRUE);
 
-  /* we don't need to set the style here, since
-   * we are a toplevel widget.
-   */
+  /* Attach the widget to the toplevel window. */
+  gtk_window_set_attached_to (GTK_WINDOW (menu->priv->toplevel), attach_widget);
 
   /* Fallback title for menu comes from attach widget */
   gtk_menu_update_title (menu);
@@ -1258,6 +1257,9 @@ gtk_menu_detach (GtkMenu *menu)
     }
   g_object_set_data (G_OBJECT (menu), I_(attach_data_key), NULL);
 
+  /* Detach the toplevel window. */
+  gtk_window_set_attached_to (GTK_WINDOW (menu->priv->toplevel), NULL);
+
   g_signal_handlers_disconnect_by_func (data->attach_widget,
                                         (gpointer) attach_widget_screen_changed,
                                         menu);
index 4d7b3bab0955959d40305316741a924d0b854879..0e4d1985eafbf4a64be31c0f31c6c63b094d9d02 100644 (file)
@@ -372,6 +372,11 @@ struct _GtkWidgetPrivate
    */
   gchar *name;
 
+  /* The list of attached windows to this widget.
+   * We keep a list in order to call reset_style to all of them,
+   * recursively. */
+  GList *attached_windows; 
+
   /* The style for the widget. The style contains the
    * colors the widget should be drawn in for each state
    * along with graphics contexts used to draw with and
@@ -8314,6 +8319,9 @@ gtk_widget_reset_style (GtkWidget *widget)
   g_return_if_fail (GTK_IS_WIDGET (widget));
 
   reset_style_recurse (widget, NULL);
+
+  g_list_foreach (widget->priv->attached_windows,
+                  (GFunc) reset_style_recurse, NULL);
 }
 
 #ifdef G_ENABLE_DEBUG
@@ -13712,6 +13720,20 @@ _gtk_widget_get_sizegroups (GtkWidget    *widget)
   return NULL;
 }
 
+void
+_gtk_widget_add_attached_window (GtkWidget    *widget,
+                                 GtkWindow    *window)
+{
+  widget->priv->attached_windows = g_list_prepend (widget->priv->attached_windows, window);
+}
+
+void
+_gtk_widget_remove_attached_window (GtkWidget    *widget,
+                                    GtkWindow    *window)
+{
+  widget->priv->attached_windows = g_list_remove (widget->priv->attached_windows, window);
+}
+
 /**
  * gtk_widget_path_append_for_widget:
  * @path: a widget path
@@ -13800,7 +13822,15 @@ gtk_widget_get_path (GtkWidget *widget)
            * where style properties might be retrieved on that
            * situation.
            */
-          widget->priv->path = gtk_widget_path_new ();
+          GtkWidget *attach_widget = NULL;
+
+          if (GTK_IS_WINDOW (widget))
+            attach_widget = gtk_window_get_attached_to (GTK_WINDOW (widget));
+
+          if (attach_widget != NULL)
+            widget->priv->path = gtk_widget_path_copy (gtk_widget_get_path (attach_widget));
+          else
+            widget->priv->path = gtk_widget_path_new ();
     
           gtk_widget_path_append_for_widget (widget->priv->path, widget);
         }
index 35f99b9c9ed33be9aa79116db3e728dfdb0b1542..241a570bf86c902ac3b661e56bc953a9fcdaae08 100644 (file)
@@ -100,6 +100,11 @@ void         _gtk_widget_remove_sizegroup      (GtkWidget    *widget,
                                                gpointer      group);
 GSList      *_gtk_widget_get_sizegroups        (GtkWidget    *widget);
 
+void         _gtk_widget_add_attached_window    (GtkWidget    *widget,
+                                                 GtkWindow    *window);
+void         _gtk_widget_remove_attached_window (GtkWidget    *widget,
+                                                 GtkWindow    *window);
+
 void _gtk_widget_override_size_request (GtkWidget *widget,
                                         int        width,
                                         int        height,
index a04d7fef604679477748fb673d0b3dcce94c4e4c..452d3e163073367c102389389076a5d668868f11 100644 (file)
@@ -105,6 +105,7 @@ struct _GtkWindowPrivate
 {
   GtkMnemonicHash       *mnemonic_hash;
 
+  GtkWidget             *attach_widget;
   GtkWidget             *default_widget;
   GtkWidget             *focus_widget;
   GtkWindow             *transient_parent;
@@ -223,6 +224,7 @@ enum {
   PROP_DELETABLE,
   PROP_GRAVITY,
   PROP_TRANSIENT_FOR,
+  PROP_ATTACHED_TO,
   PROP_OPACITY,
   PROP_HAS_RESIZE_GRIP,
   PROP_RESIZE_GRIP_VISIBLE,
@@ -967,6 +969,21 @@ gtk_window_class_init (GtkWindowClass *klass)
                                                        GTK_TYPE_WINDOW,
                                                        GTK_PARAM_READWRITE| G_PARAM_CONSTRUCT));
 
+  /**
+   * GtkWindow:attached-to:
+   *
+   * the widget attached to the window.
+   *
+   * Since: 3.4
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_ATTACHED_TO,
+                                   g_param_spec_object ("attached-to",
+                                                        P_("Attached to Widget"),
+                                                        P_("The widget where the window is attached"),
+                                                        GTK_TYPE_WIDGET,
+                                                        GTK_PARAM_READWRITE| G_PARAM_CONSTRUCT));
+
   /**
    * GtkWindow:opacity:
    *
@@ -1277,6 +1294,9 @@ gtk_window_set_property (GObject      *object,
     case PROP_TRANSIENT_FOR:
       gtk_window_set_transient_for (window, g_value_get_object (value));
       break;
+    case PROP_ATTACHED_TO:
+      gtk_window_set_attached_to (window, g_value_get_object (value));
+      break;
     case PROP_OPACITY:
       gtk_window_set_opacity (window, g_value_get_double (value));
       break;
@@ -1398,6 +1418,9 @@ gtk_window_get_property (GObject      *object,
     case PROP_TRANSIENT_FOR:
       g_value_set_object (value, gtk_window_get_transient_for (window));
       break;
+    case PROP_ATTACHED_TO:
+      g_value_set_object (value, gtk_window_get_attached_to (window));
+      break;
     case PROP_OPACITY:
       g_value_set_double (value, gtk_window_get_opacity (window));
       break;
@@ -2375,6 +2398,20 @@ gtk_window_list_toplevels (void)
   return list;
 }
 
+static void
+remove_attach_widget (GtkWindow *window)
+{
+  GtkWindowPrivate *priv = window->priv;
+
+  if (priv->attach_widget)
+    {
+      _gtk_widget_remove_attached_window (priv->attach_widget, window);
+
+      g_object_unref (priv->attach_widget);
+      priv->attach_widget = NULL;
+    }
+}
+
 static void
 gtk_window_dispose (GObject *object)
 {
@@ -2383,6 +2420,8 @@ gtk_window_dispose (GObject *object)
   gtk_window_set_focus (window, NULL);
   gtk_window_set_default (window, NULL);
 
+  remove_attach_widget (GTK_WINDOW (object));
+
   G_OBJECT_CLASS (gtk_window_parent_class)->dispose (object);
 }
 
@@ -2573,6 +2612,69 @@ gtk_window_get_transient_for (GtkWindow *window)
   return window->priv->transient_parent;
 }
 
+/**
+ * gtk_window_set_attached_to:
+ * @window: a #GtkWindow
+ * @attach_widget (allow-none): a #GtkWidget, or %NULL
+ *
+ * Attach the window to a widget. Indicates that @window belongs to @widget.
+ * For example the window of a GtkMenu could belong to a GtkMenuBar or
+ * a GtkEntry or a GtkComboBox, and so on...
+ *
+ * GTK will use this information for styling the @window,
+ * for presenting it as a child of @widget in the accessibility tree
+ * and other things.
+ *
+ * Passing %NULL for @attach_widget detaches the window.
+ *
+ * Since: 3.4
+ **/
+void
+gtk_window_set_attached_to (GtkWindow *window,
+                            GtkWidget *attach_widget)
+{
+  GtkWindowPrivate *priv;
+
+  g_return_if_fail (GTK_IS_WINDOW (window));
+  g_return_if_fail (GTK_WIDGET (window) != attach_widget);
+
+  priv = window->priv;
+
+  remove_attach_widget (window);
+
+  priv->attach_widget = attach_widget;
+
+  if (priv->attach_widget)
+    {
+      _gtk_widget_add_attached_window (priv->attach_widget, window);
+
+      g_object_ref_sink (priv->attach_widget);
+    }
+
+  /* Update the style, as the widget path might change. */
+  gtk_widget_reset_style (GTK_WIDGET (window)); 
+}
+
+/**
+ * gtk_window_get_attached_to:
+ * @window: a #GtkWindow
+ *
+ * Fetches the attach widget for this window. See
+ * gtk_window_set_attached_to().
+ *
+ * Return value: (transfer none): the widget where the window is attached,
+ *   or %NULL if the window is not attached to any widget.
+ * 
+ * Since: 3.4
+ **/
+GtkWidget *
+gtk_window_get_attached_to (GtkWindow *window)
+{
+  g_return_val_if_fail (GTK_IS_WINDOW (window), NULL);
+
+  return window->priv->attach_widget;
+}
+
 /**
  * gtk_window_set_opacity:
  * @window: a #GtkWindow
@@ -4603,6 +4705,8 @@ gtk_window_destroy (GtkWidget *widget)
   if (priv->transient_parent)
     gtk_window_set_transient_for (window, NULL);
 
+  remove_attach_widget (GTK_WINDOW (widget));
+
   /* frees the icons */
   gtk_window_set_icon_list (window, NULL);
 
index 17203070e220522afdc9dafe9a6b9a12bf3a786d..3fcc5b22125e4a80370154961379959cc4fc02d0 100644 (file)
@@ -135,6 +135,9 @@ gboolean   gtk_window_activate_default             (GtkWindow           *window);
 void       gtk_window_set_transient_for        (GtkWindow           *window, 
                                                GtkWindow           *parent);
 GtkWindow *gtk_window_get_transient_for        (GtkWindow           *window);
+void       gtk_window_set_attached_to          (GtkWindow           *window, 
+                                                GtkWidget           *attach_widget);
+GtkWidget *gtk_window_get_attached_to          (GtkWindow           *window);
 void       gtk_window_set_opacity              (GtkWindow           *window, 
                                                gdouble              opacity);
 gdouble    gtk_window_get_opacity              (GtkWindow           *window);